package UBit (
              UBit,
              mkUBitReg,
              integerToUBit, -- in place of Literal instance
              uBitToInteger, -- the other way!!
              uBitSelect, uBitUpdate, -- in place of PrimSelectable
              uBitConcat -- other functions like this can be defined
              ) where

import ListReg
import Vector
import List

-- The UBit type
data UBit = UBit (List (Bit 1))


integerToUBit :: Integer -> Integer -> UBit
integerToUBit sz i = UBit (errIntegerToBitList "integerToUBit" sz i)


mkUBitReg :: (IsModule m c) => Integer -> Integer -> m (Reg UBit)
mkUBitReg sz init_val =
  module
    let init_list = errIntegerToBitList "mkUBitReg" sz init_val

    reg :: Reg (List (Bit 1))
    reg <- mkListReg init_list

    interface

      _read  = bitListToUBit reg._read

      _write (UBit bs) = reg._write bs


-- Instances:

{-
-- is this bad?  To have it work for any k?
instance Bits UBit k
  where
    pack x = uBitToBit x
    unpack x = bitToUBit x
-}

-- XXX What if the lists are of different size?
instance Eq UBit
  where
   (==) (UBit xs) (UBit ys) = (xs == ys)
   (/=) (UBit xs) (UBit ys) = (xs /= ys)

compareUBit :: (Bit 1 -> Bit 1 -> Bool) -> Bool -> UBit -> UBit -> Bool
compareUBit op chkEqual (UBit bs1) (UBit bs2) =
    let f :: List (Bit 1) -> List (Bit 1) -> Bool
        f Nil Nil = chkEqual
        f (Cons x xs) (Cons y ys) = (x `op` y) || ((x == y) && (f xs ys))
    in  f bs1 bs2

instance Ord UBit
  where
   (<)  x y = compareUBit (<) False x y
   (<=) x y = compareUBit (<) True  x y
   (>)  x y = compareUBit (>) False x y
   (>=) x y = compareUBit (>) True  x y

zipWithErr :: String -> (Bit 1 -> Bit 1 -> Bit 1) -> UBit -> UBit -> UBit
zipWithErr name f (UBit xs) (UBit ys) =
    if ((length xs) == (length ys))
    then UBit (List.zipWith f xs ys)
    else -- either zeroExtend one or give an error
         error ("bitwise UBit operation " +++ name +++
                " failed because the bit sizes are not equal: " +++
                integerToString (length xs) +++ " /= " +++
                integerToString (length ys))

instance Bitwise UBit
  where
    (&) x y = zipWithErr "&" (&) x y
    (|) x y = zipWithErr "|" (|) x y
    (^) x y = zipWithErr "^" (^) x y
    (~^) x y = zipWithErr "~^" (~^) x y
    (^~) x y = zipWithErr "^~" (^~) x y
    invert (UBit xs) = UBit (List.map invert xs)
    (<<) x y = error "UBit: << not yet implemented"
    (>>) x y = error "UBit: >> not yet implemented"

-- We have the option here of extending when necessary...
addUBit :: String -> (Bit 2 -> Bit 2 -> Bit 2) -> UBit -> UBit -> UBit
addUBit name op (UBit xs) (UBit ys) =
    let
        addBit :: Bit 1 -> Bit 1 -> Bit 1 -> (Bit 1, Bit 1)
        addBit x y c =
          let twobitsum :: Bit 2
              twobitsum = ((zeroExtend x) `op` (zeroExtend y))
                           `op` (zeroExtend c)
          in  split twobitsum

        f (x,y) (rs,carrybit) =
          let (c,b) = addBit x y carrybit
          in  (Cons b rs, c)
    in
        if ((length xs) /= (length ys))
        then error ("UBit operation " +++ name +++
                    " failed because the bit sizes are not equal: " +++
                    integerToString (length xs) +++ " /= " +++
                    integerToString (length ys))
        else UBit (List.foldr f (Nil,0) (List.zip xs ys)).fst

instance Arith UBit
  where
    (+) x y = addUBit "+" (+) x y
    (-) x y = addUBit "-" (-) x y
    negate x@(UBit xs) = let zero = List.map (const 0) (upto 0 (length xs - 1))
                         in  addUBit "-" (-) (UBit zero) x
    (*) x y = error "UBit: * not yet implemented"

uBitSelect :: List a -> UBit -> a
uBitSelect l (UBit k) =
    let f p res = if (k == p.snd) then p.fst else res
        zeroBits = List.map (const 0) k
    in  List.foldr f _ (numListBits l zeroBits)

uBitUpdate :: List a -> UBit -> a -> List a
uBitUpdate l (UBit k) x =
    let f p = if (k == p.snd) then x else p.fst
        zeroBits = List.map (const 0) k
    in  List.map f (numListBits l zeroBits)

numListBits :: List a -> List (Bit 1) -> List (a, List (Bit 1))
numListBits Nil bs = Nil
numListBits (Cons x xs) bs = Cons (x, bs) (numListBits xs (incrListBits bs))

incrListBits :: List (Bit 1) -> List (Bit 1)
incrListBits bs =
    let
        addBit :: Bit 1 -> Bit 1 -> (Bit 1, Bit 1)
        addBit b c =
          let twobitsum :: Bit 2
              twobitsum = (zeroExtend b) + (zeroExtend c)
          in  split twobitsum

        f x (rs,carrybit) =
          let (c,b) = addBit x carrybit
          in  (Cons b rs, c)
    in
        (List.foldr f (Nil,1) bs).fst

{-
-- This is not possible becase of fundeps on PrimSelectable:
-- List already has an instance, so we can't define another
instance PrimSelectable (List a) UBit a
  where
    primSelectFn = uBitSelect
    primUpdateFn = uBitUpdate
-}

{-
-- we can't define a Literal class in this version of UBit;
-- use integerToUBit
-- If size info about literals were not thrown away (such as
-- the 12 in 12'b0, which applied to a SizedLiteral class,
-- then we could define an instance of SizedLiteral for UBit.
instance Literal UBit
  where
    fromInteger x = integerToUBit ??? x

-- similarly for bounded;
-- we would have to use a bounded function with a size parameter,
-- or a representation of UBit which allowed a tagged disjoint
-- which was minBound and one which was maxBound
instance Bounded UBit
  where
    minBound = ???
    maxBound = ???
-}

instance Literal UBit
  where
    fromInteger x = error "fromInteger not defined for UBit"

-- Can define other functions like this:
uBitConcat :: UBit -> UBit -> UBit
uBitConcat (UBit xs) (UBit ys) = UBit (List.append xs ys)

-- Can also include:
-- * split?
-- * BitExtend class
-- * PrimSelectable class
-- etc


-- try using "pack" instead of this
uBitToBit :: UBit -> Bit n
uBitToBit (UBit xs) =
    let ubsize = length xs
        vecsize = valueOf n
    in  if (ubsize /= vecsize)
        then error ("uBitToBit: cannot cast UBit of size " +++
                    integerToString ubsize +++
                    " into a Bit vector of size " +++
                    integerToString vecsize)
        else pack (toVector xs)


-- try using "unpack" instead of this
bitToUBit :: Bit n -> UBit
bitToUBit bs =
    let bsN :: Vector n (Bit 1)
        bsN = unpack bs
    in  UBit (toList bsN)


-- Assumes that the size and the value are non-negative
integerToBitList :: Integer -> Integer -> Maybe (List (Bit 1))
integerToBitList sz val =
  let
      f :: Integer -> Integer -> List (Bit 1) -> Maybe (List (Bit 1))
      f sz val accum =
          if (sz == 0)
          then if (val > 0) then Invalid else Valid accum
          else let b = val `mod` 2
                   r = val `div` 2
               in  f (sz - 1) r (Cons (fromInteger b) accum)
  in  f sz val Nil


errIntegerToBitList :: String -> Integer -> Integer -> List (Bit 1)
errIntegerToBitList fname sz val =
  let prefx = if (fname == "") then fname else fname +++ ": "
  in  if (sz < 0)
      then error (prefx +++
                  "UBit must have non-negative size: given size " +++
                  integerToString sz)
      else if (val < 0)
      then error (prefx +++
                  "UBit must have non-negative value: given value " +++
                  integerToString val)
      else case (integerToBitList sz val) of
               Invalid -> error (prefx +++ "initial value " +++
                                 integerToString val +++
                                 " too large for vector of " +++
                                integerToString sz +++ " bits")
               Valid res -> res


uBitToInteger :: UBit -> Integer
uBitToInteger (UBit xs) =
    let
        f :: (Bit 1) -> (Integer, Integer) -> (Integer, Integer)
        f bit (tot,fac) = let bi = if (bit == 1) then 1 else 0
                          in  (tot + (bi * fac), fac * 2)
    in
        (List.foldr f (0,1) xs).fst

bitListToUBit :: List (Bit 1) -> UBit
bitListToUBit bs = UBit bs


